package at.technikum_wien.embsys.aat.PriscillaCore.component;

import java.util.Iterator;
import java.util.List;

import at.technikum_wien.embsys.aat.PriscillaCore.enums.EnoceanComponentType;
import at.technikum_wien.embsys.aat.PriscillaCore.event.FrameEventTemperature;
import at.technikum_wien.embsys.aat.PriscillaCore.link.event.LinkListener;
import at.technikum_wien.embsys.aat.PriscillaCore.telegram.Telegram;



/**
 * 
 * EEP_07_09_01 component class - CO gas and temperature sensor
 *
 * Type: 8bit temperature (may not be available) and CO gas sensor
 * Temperature max: 255°C
 * Temperature min: 0°C
 * CO concentration max: 255ppm
 * CO concentration min: 0ppm
 * 
 * @author B. Aigner
 * 
 */
public class EEP_07_09_01 implements EnoceanComponent {
	
	Telegram telegram;
	List<?> eventListener;
	float concentration = -300F;
	float temperature = -300F;
	
	boolean temperatureAvailable = false;
	
	EnoceanComponentType enoceanComponentType = EnoceanComponentType.EEP_07_09_01;
	boolean telegramCreation = false;
	
	int concentration_temp = 0;
	float concentration_max = 255;
	float concentration_min = 0;
	int concentration_value_max = 255;
	int concentration_value_min = 0;
	
	int temperature_temp = 0;
	float temperature_max = 255;
	float temperature_min = 0;
	int temperature_value_max = 255;
	int temperature_value_min = 0;
	
	float multiplier;

	/**
	 * This constructor is used for received packets.
	 * It will parse the telegram and set the corresponding members.
	 * 
	 * @param telegram Received Telegram
	 * @param listener Event subscriber list
	 */
	public EEP_07_09_01(Telegram telegram, List<?> listener) {
		super();
		this.telegram = telegram;
		this.eventListener = listener;
	
		if(telegram.getData2() != telegram.getData3()) {
			//System.out.println("Transmit error, gas sensor value is not valid");
			return;
		}
		concentration_temp = telegram.getData2();

		if(checkBit(telegram.getData0(),1)) {
			temperatureAvailable = true;
			temperature_temp = telegram.getData1();
		}
	
		multiplier = (concentration_max - concentration_min) / (concentration_value_max - concentration_value_min);
		
		if(multiplier < 0) {
			this.concentration = multiplier * concentration_temp + concentration_max;
		} else {
			this.concentration = multiplier * concentration_temp + concentration_min;
		}

		if(this.temperatureAvailable == true) {
			multiplier = (temperature_max - temperature_min) / (temperature_value_max - temperature_value_min);
			
			if(multiplier < 0) {
				this.temperature = multiplier * temperature_temp + temperature_max;
			} else {
				this.temperature = multiplier * temperature_temp + temperature_min;
			}
		}

		fireFrame();
	}

	/**
	 * Fire all associated events
	 */
	void fireFrame() {
		Iterator<?> listen = eventListener.iterator();
		
		//TODO: Gas Event einbauen
		/*final FrameEventGas frameEventGas = new FrameEventGas(this, telegram.getDeviceID(), this.concentration);
		while (listen.hasNext()) {
			((LinkListener) listen.next()).frameReceived_Gas(frameEventGas);
		}*/
		
		if(temperatureAvailable) {
			final FrameEventTemperature frameEventTemperature = new FrameEventTemperature(this, telegram.getDeviceID(), this.temperature);
			while (listen.hasNext()) {
				((LinkListener) listen.next()).frameReceived_Temperature(frameEventTemperature);
			}
		}
	}

	/**
	 * Void constructor. It is used to create a new telegram for sending
	 */
	public EEP_07_09_01() {
		super();
		telegram = new Telegram();
		telegramCreation = true;
	}

	/**
	 * This constructor is used to create a telegram (the device id is given as parameter)
	 * @param deviceID Full device ID
	 */
	public EEP_07_09_01(String deviceID) {
		super();
		telegram = new Telegram();
		telegram.setDeviceID(deviceID);
		telegramCreation = true;
	}
	
	boolean checkBit(int value, int bitNr) {
		return (value & (1 << bitNr)) != 0;
	}
	
	int setBit(int value, int bitNr) {
		return value | (1<<bitNr);
	}
	
	int clrBit(int value, int bitNr) {
		return value & (~(1<<bitNr));
	}
	
	@Override
	public String getDeviceID() {
		return telegram.getDeviceID();
	}
	@Override
	public String getORG() {
		return telegram.getxORG();
	}

	@Override
	public EnoceanComponentType getType() {
		return enoceanComponentType;
	}

	@Override
	public void setDeviceID(String deviceID) {
		telegram.setDeviceID(deviceID);
	}

	/**
	 * Creates a new telegram from the component
	 * 
	 * This method creates a new telegram (if the component is initialized with
	 * a constructor without a telegram as parameter) or returns the saved telegram.
	 * The new telegram is built up from the members in this class (in this case temperature and gas concentration). 
	 * @return Created telegram
	 */
	public Telegram getTelegram() {
		if(telegramCreation == true) {
			multiplier = (temperature_max - temperature_min) / (temperature_value_max - temperature_value_min);
			if(multiplier < 0) {
				telegram.setData1((int)((this.temperature - temperature_max) / multiplier));
			} else {
				telegram.setData1((int)((this.temperature - temperature_min) / multiplier));
			}
			
			multiplier = (concentration_max - concentration_min) / (concentration_value_max - concentration_value_min);
			if(multiplier < 0) {
				telegram.setData2((int)((this.concentration - concentration_max) / multiplier));
			} else {
				telegram.setData2((int)((this.concentration - concentration_min) / multiplier));
			}
			telegram.setData3(telegram.getData2());
			
			if(temperatureAvailable == true) {
				telegram.setData0(setBit(telegram.getData0(),1));
			} else {
				telegram.setData0(clrBit(telegram.getData0(),1));
			}
		}
		return telegram;
	}

	public float getConcentration() {
		return concentration;
	}

	public float getTemperature() {
		return temperature;
	}
	
	public boolean isTemperatureAvailable() {
		return temperatureAvailable;
	}

	public void setConcentration(float concentration) {
		if(concentration > concentration_max) return;
		if(concentration < concentration_min) return;
		this.concentration = concentration;
	}

	public void setTemperature(float temperature) {
		if(temperature > temperature_max) return;
		if(temperature < temperature_min) return;
		this.temperature = temperature;
	}

	public void setTemperatureAvailable(boolean temperatureAvailable) {
		this.temperatureAvailable = temperatureAvailable;
	}
}
